using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Security.Principal;
using System.Web;
using System.Xml.Serialization;

namespace DE.Sdc.SharePoint.Common.Deployment
{
    [Serializable]
    public abstract class BaseItem<TParent> : NamedElement<TParent>, IBaseItem where TParent : NamedElement, IBaseItem
    {
        /// <remarks/>
        [XmlArrayItem("Property")]
        public List<Property> Properties { get; set; }

        //[XmlArrayItem("Permission")]
        [XmlElement(typeof(Permissions))]
        public Permissions Permissions { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string Url { get; set; }

        [XmlAttribute]
        public string AbsoluteUrl
        {
            get
            {
                if (GetType() == typeof(ContentStructure))
                    return "";

                if (Parent != null)
                    return Parent.AbsoluteUrl + (String.IsNullOrEmpty(Parent.AbsoluteUrl) ? "" : "/") + (Url ?? Title);

                throw new ArgumentNullException("Can not resolve AbsoluteUrl. No parent specified for '" + Title + "'");
            }
        }

        public Hashtable GetProperiesAsHashtable(CultureInfo culture)
        {
            var properies = new Hashtable();

            if (Properties != null)
            {
                for (int index = 0; index < Properties.Count; index++)
                {
                    Property property = Properties[index];
                    properies.Add(property.Name, ResolveResource(property.Value, culture));
                }
            }

            return properies;
        }


        /// <summary>
        /// Modifies the properties of an object
        /// </summary>
        /// <param name="itemDefinition">Xml object for an IBaseItem</param>
        /// <param name="objectToModifyProperties">The properties object</param>
        protected static void ModifyObjectProperties(IBaseItem itemDefinition, object objectToModifyProperties)
        {
            Trace.WriteLine("Try to modify object properties '" + itemDefinition.Title + "' with url '" + itemDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:ModifyObjectProperties");

            Type objectType = objectToModifyProperties.GetType();

            for (int index = 0; index < itemDefinition.Properties.Count; index++)
            {
                Property property = itemDefinition.Properties[index];
                PropertyInfo propertyInfo = objectType.GetProperty(property.Name);

                if (propertyInfo == null)
                    throw new ContentStructureImporterException("Can not modify property '" + property.Name + "' because it does not exist in the object of type '" + objectType.Name + "'");

                object currentValue = propertyInfo.GetValue(objectToModifyProperties, null);

                if (currentValue != null && currentValue.ToString() != property.Value ||
                    currentValue == null && property.Value != null)
                {
                    object newValue = property.Value;

                    if (propertyInfo.PropertyType.IsEnum)
                        newValue = Enum.Parse(propertyInfo.PropertyType, property.Value);
                    else if (propertyInfo.PropertyType != typeof(string))
                        newValue = Convert.ChangeType(newValue, propertyInfo.PropertyType);

                    propertyInfo.SetValue(objectToModifyProperties, newValue, null);
                    Trace.WriteLine("Change property '" + property.Name + "' of item '" + itemDefinition.Title ?? itemDefinition.Url + "'");
                }
            }

            Trace.WriteLine("Object properties modified '" + itemDefinition.Title + "' with url '" + itemDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:ModifyObjectProperties");
        }

        /// <summary>
        /// Adds or updates properties in a given hashtable
        /// </summary>
        /// <param name="propertyKey">Name of the property</param>
        /// <param name="propertyValue">Value of the property</param>
        /// <param name="modifyIfPropertyValueIsNull">Indicates whether the property can be updated with null-values or not</param>
        /// <param name="properties">The property hashtable</param>
        protected static internal void ModifyProperty(string propertyKey, object propertyValue, bool modifyIfPropertyValueIsNull, Hashtable properties)
        {
            if (properties == null)
                return;

            if (!modifyIfPropertyValueIsNull && propertyValue == null)
                return;

            Trace.WriteLine("Try to modify property '" + propertyKey + "' with value '" + propertyValue + "'.", "ContentStructureImporter:ModifyProperty");
            if (properties.ContainsKey(propertyKey))
                properties[propertyKey] = propertyValue;
            else
                properties.Add(propertyKey, propertyValue);
            Trace.WriteLine("Property '" + propertyKey + "' with value '" + propertyValue + "' modified.", "ContentStructureImporter:ModifyProperty");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="url"></param>
        /// <param name="parentUrl"></param>
        /// <returns></returns>
        protected static string GetRootedUrl(string url, string parentUrl)
        {
            if (!string.IsNullOrEmpty(url))
            {
                if (url.StartsWith("/") || url.StartsWith("http") || url.StartsWith(@"\\"))
                    return url;

                return parentUrl.TrimEnd('/') + "/" + url;
            }

            return null;
        }

        /// <summary>
        /// Replaces the namespace in a given input string
        /// </summary>
        /// <param name="input">A string where the namespace should replaced</param>
        /// <param name="namespaceUri">A string with the namespace value</param>
        /// <returns>The cleaned up string</returns>
        protected static string RemoveNamespace(string input, string namespaceUri)
        {
            return input.Replace("xmlns=\"" + namespaceUri + "\"", "");
        }

        /// <summary>
        /// Creates a new http context from a given context url
        /// </summary>
        /// <param name="contextUrl">Absolute http url which context should used</param>
        protected internal void CreateContext(string contextUrl)
        {
            Trace.WriteLine("Try to create context.", "ContentStructureImporter:CreateContext");

            if (ContentStructureImporter.CurrentContextInitiallySet)
                return;

            HttpRequest request = new HttpRequest(System.IO.Path.GetFileName(contextUrl), contextUrl, null);

            //var wrapper = new HttpBrowserCapabilitiesWrapper(new HttpBrowserCapabilities());
            request.Browser = new HttpBrowserCapabilities();

            HttpResponse response = new HttpResponse(new StringWriter());

            HttpContext context = new HttpContext(request, response);

            WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent();

            if (currentIdentity != null)
                context.User = new WindowsPrincipal(currentIdentity);

            HttpContext.Current = context;

            //if (_currentContextProperty == null)
            //    _currentContextProperty = typeof(HttpContext).GetProperty("Current",
            //                                                     BindingFlags.Public | BindingFlags.Static |
            //                                                     BindingFlags.SetProperty);
            //_currentContextProperty.SetValue(null, context, null);

            Trace.WriteLine("Context created.", "ContentStructureImporter:CreateContext");
        }

        /// <summary>
        /// Deletes the current http context
        /// </summary>
        protected internal void ClearContext()
        {
            Trace.WriteLine("Try to clear context.", "ContentStructureImporter:ClearContext");


            if (ContentStructureImporter.CurrentContextInitiallySet)
                return;

            HttpContext.Current = null;
            //if (_currentContextProperty == null)
            //    _currentContextProperty = typeof(HttpContext).GetProperty("Current",
            //                                                     BindingFlags.Public | BindingFlags.Static |
            //                                                     BindingFlags.SetProperty);
            //_currentContextProperty.SetValue(null, null, null);

            Trace.WriteLine("Context cleared.", "ContentStructureImporter:ClearContext");
        }

        
    }
}